-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

-- Description: imu通信的相关工具
local ipmi = require 'ipmi'
local class = require 'mc.class'
local enums = require 'ipmi.enums'
local singleton = require 'mc.singleton'
local bs = require 'mc.bitstring'
local log = require 'mc.logging'
local skynet = require 'skynet'

local channel_type = enums.ChannelType
local comp_code = ipmi.types.Cc

local SINGLE_HOST<const> = 1
local Imu = class()

function Imu:ctor(bus)
    self.bus = bus
    self.instance = {}
end

-- 添加instance信息，不同host的instance不一样
function Imu:add_instances(instance)
    skynet.fork(function()
        for _, config in ipairs(instance.Maps) do
            self.instance[config[1]] = config[2]
        end
        log:notice('[imu]add instance finish')
    end)
end

function Imu:is_single(msg)
    if not msg.SystemId then
        return true
    end
    if msg.SystemId == SINGLE_HOST and not self.instance[SINGLE_HOST] then
        return true
    end
    return false
end

function Imu:singlehost_send(header, send_data)
    return ipmi.request(self.bus, channel_type.CT_ME:value(), {
        DestNetFn = header.DestNetFn,
        Cmd = header.Cmd,
        Payload = send_data
    })
end

function Imu:multihost_send(header, send_data, system_id)
    local instance = self.instance[system_id]
    if not self.instance[system_id] then
        error('[bios]imu:multihost send fail')
    end
    return ipmi.request(self.bus, {
        channel_type.CT_ME:value(), instance}, {
            DestNetFn = header.DestNetFn,
            Cmd = header.Cmd,
            Payload = send_data
        }
    )
end

function Imu:send(msg)
    if not msg or not msg.Header or not msg.Data or not msg.SendFormat then
        error('[bios]imu:invalid param')
    end
    local header = msg.Header
    local send_data = bs.new(msg.SendFormat):pack(msg.Data)
    local cc, payload
    if self:is_single(msg) then
        cc, payload = self:singlehost_send(header, send_data)
    else
        cc, payload = self:multihost_send(header, send_data, msg.SystemId)
    end
    if cc == comp_code.Success then
        return payload
    end
    error(string.format('[bios]imu:send fail, res(%s)', cc))
end

function Imu:send_and_receive(msg)
    if not msg or not msg.ReceiveFormat then
        error('[bios]imu:invalid param')
    end
    return bs.new(msg.ReceiveFormat):unpack(self:send(msg))
end

function Imu:send_with_error_code(msg)
    if not msg or not msg.Header or not msg.Data or not msg.SendFormat then
        error('[bios]imu:invalid param')
    end
    local header = msg.Header
    local send_data = bs.new(msg.SendFormat):pack(msg.Data)
    local cc, payload
    if self:is_single(msg) then
        cc, payload = self:singlehost_send(header, send_data)
    else
        cc, payload = self:multihost_send(header, send_data, msg.SystemId)
    end
    if cc == comp_code.Success then
        return payload
    end
    error({Code = cc})
end

return singleton(Imu)